/**
 * Copyright nikukyu ( http://wonderfl.net/user/nikukyu )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/lXk5
 */

/**
<1にょっ木 - 桜咲け！>
画像を使わずに木を生成してみました。    
受験のシーズンということで、桜（っぽい何か）です。
    
　画面をクリックするとはげた土が出来ます。
　はげた土をクリックすると木が生えます。
　木が生えた土をクリックすると木が消えます。
　木が生える時に、その土はプライオリティが一番手前にきます（なぜか）。

　たまに咲きすぎて重くなります。頑張れ受験生...


<1NyoKi - BLOOM Cherry Blossoms(like)!>

　click stage to generate free ground.
　click free ground to generate tree.
　click planted ground to remove tree.
　generating tree will set the ground priority as top.

　sometimes too heavy bloom cause frame rate issue. 
*/
package
{
    import flash.display.Sprite;
    import flash.events.MouseEvent;
    
    [SWF(backgroundColor='#ffffff', frameRate='30', width='465', height='465')]     
    
    public class Main extends Sprite 
    {
        public function Main()
        {
            stage.addEventListener( MouseEvent.CLICK, onClickStage );
        }
        
        private function onClickStage( e:MouseEvent ):void 
        {
            var nyo_ground:NyoGround    = new NyoGround();
            nyo_ground.x                = e.stageX;
            nyo_ground.y                = e.stageY + 10;
            addChild( nyo_ground );
        }
    }
}

import flash.display.GradientType;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Matrix;
import flash.geom.Point;
import idv.cjcat.stardust.common.actions.Age;
import idv.cjcat.stardust.common.actions.AlphaCurve;
import idv.cjcat.stardust.common.actions.DeathLife;
import idv.cjcat.stardust.common.actions.ScaleCurve;
import idv.cjcat.stardust.common.clocks.ImpulseClock;
import idv.cjcat.stardust.common.emitters.Emitter;
import idv.cjcat.stardust.common.initializers.CollisionRadius;
import idv.cjcat.stardust.common.initializers.Life;
import idv.cjcat.stardust.common.initializers.Mass;
import idv.cjcat.stardust.common.initializers.Scale;
import idv.cjcat.stardust.common.math.UniformRandom;
import idv.cjcat.stardust.twoD.actions.Damping;
import idv.cjcat.stardust.twoD.actions.Gravity;
import idv.cjcat.stardust.twoD.actions.Move;
import idv.cjcat.stardust.twoD.actions.Spin;
import idv.cjcat.stardust.twoD.display.AddChildMode;
import idv.cjcat.stardust.twoD.emitters.Emitter2D;
import idv.cjcat.stardust.twoD.fields.Field;
import idv.cjcat.stardust.twoD.fields.UniformField;
import idv.cjcat.stardust.twoD.handlers.DisplayObjectHandler;
import idv.cjcat.stardust.twoD.initializers.DisplayObjectClass;
import idv.cjcat.stardust.twoD.initializers.Omega;
import idv.cjcat.stardust.twoD.initializers.Position;
import idv.cjcat.stardust.twoD.initializers.Velocity;
import idv.cjcat.stardust.twoD.zones.LazySectorZone;
import idv.cjcat.stardust.twoD.zones.SinglePoint;

class NyoGround extends Sprite
{
    private var _step:int;
    private var _anchor_point:Array;
    private var _control_point:Array;
	
	private var emitters:Vector.<Emitter> = new Vector.<Emitter>();
    
    public function NyoGround()
    {
        super();
                    
        var m:Matrix                    = new Matrix();
        m.createGradientBox( 100, 30, 90 * Math.PI / 180, 0, -30 );
        graphics.beginGradientFill( GradientType.LINEAR, [ 0x804000, 0x804000 ], [ 1, 0 ], [ 172, 255 ], m );
        
        graphics.moveTo( -50, 0 );
        graphics.curveTo( 0, -50, 50, 0 );
        graphics.endFill();

        addEventListener( MouseEvent.CLICK, onClickGround );
		addEventListener(Event.ENTER_FRAME, stepEmitters);
    }
	
	private function stepEmitters(e:Event):void {
		var emptyEmitters:Vector.<Emitter> = new Vector.<Emitter>();
		var i:int, len:int;
		var emitter:Emitter;
		for (i = 0, len = emitters.length; i < len; ++i) {
			emitter = emitters[i];
			emitter.step();
			if (emitter.numParticles == 0) {
				emptyEmitters.push(emitter);
			}
		}
		for (i = 0, len = emptyEmitters.length; i < len; ++i) {
			emitter = emptyEmitters[i];
			emitters.splice(emitters.indexOf(emitter), 1);
		}
	}
    
    private function onClickGround( e:MouseEvent ):void 
    {
        if ( numChildren )
        {
            removeChildAt( 0 );
        }
        else
        {	
            var nyo_tree:NyoTree        = new NyoTree();
            nyo_tree.y                    = -10;
            addChild( nyo_tree );
            parent.setChildIndex( this, parent.numChildren - 1 );
			
			//emitter
			var emitter:Emitter = new SakuraEmitter(nyo_tree);
			emitters.push(emitter);
        }
        
        // stop event-notice
        e.stopPropagation();
    }
}

class SakuraEmitter extends Emitter2D {
	
	private var impulseClock:ImpulseClock = new ImpulseClock(70, 5);
	
	public function SakuraEmitter(container:Sprite) {
		super(impulseClock);
		impulseClock.impulse();
		
		var lazySector:LazySectorZone = new LazySectorZone(60, 50);
		lazySector.direction.set(0, -1);
		lazySector.directionVar = 40;
		
		//initializers
		addInitializer(new DisplayObjectClass(NyoLeaves, [1]));
		addInitializer(new Life(new UniformRandom(40, 20)));
		addInitializer(new Velocity(lazySector));
		addInitializer(new Omega(new UniformRandom(0, 20)));
		addInitializer(new Scale(new UniformRandom(2, 1.5)));
		addInitializer(new Mass(new UniformRandom(1, 0.5)));
		
		//actions
		addAction(new Age());
		addAction(new DeathLife());
		addAction(new Damping(0.2));
		addAction(new Move());
		addAction(new Spin());
		addAction(new ScaleCurve(5, 20));
		
		//gravity
		var field:Field = new UniformField(0, 0.5);
		field.massless = false;
		var gravity:Gravity = new Gravity();
		gravity.addField(field);
		addAction(gravity);
		
		//handler
		particleHandler = new DisplayObjectHandler(container, AddChildMode.TOP);
	}
}

class NyoTree extends Sprite
{
    // Life
    private const MAX_LIFE:int                    = 18;
    private const MIN_LIFE:int                    = 2;
    private const LIFE_DEGENERATE_RATE:int        = 3;
    
    // Branch
    private const BRANCH_MIN_LENGTH:int            = 10;
    private const BRANCH_LENGTH_RATE:int        = 5;
    private const BRANCH_SPREAD_RATE:int        = 30;
    private const BRANCH_CONTINUOUS:int            = 20;
    private const BRAHCN_THICKNESS:int            = 2;
    
    // variable
    private var _life:int;
    private var _branch_point:Point;
    private var _rotate:int;
	
    public function NyoTree( life:int = MAX_LIFE, parent_rotate:int = -180 )
    {
        // adjust life
        _life                            = ( life < 0 ) ? 0 : life;
        
        // generate BranchPoint
        _branch_point                    = new Point( 0, BRANCH_MIN_LENGTH + Math.floor( Math.random() * life * BRANCH_LENGTH_RATE ) );

        // calculate Vertex
        var point_l:Point                = new Point( - life * BRAHCN_THICKNESS / 10, 0 );
        var point_tl:Point                = new Point( - life * BRAHCN_THICKNESS / 15, _branch_point.y );
        var point_tr:Point                = new Point( life * BRAHCN_THICKNESS / 15, _branch_point.y );
        var point_r:Point                = new Point( life * BRAHCN_THICKNESS / 10, 0 );
        
        // decide Rotation
        var r:int                        = -BRANCH_SPREAD_RATE + Math.floor( Math.random() * BRANCH_SPREAD_RATE * 2 );
        _rotate                            = parent_rotate + r;                            
        
        // apply Rotation
        var m:Matrix                    = new Matrix();
        m.rotate( _rotate * Math.PI / 180 );
        _branch_point                    = m.deltaTransformPoint( _branch_point );
        point_l                            = m.deltaTransformPoint( point_l );
        point_tl                        = m.deltaTransformPoint( point_tl );
        point_tr                        = m.deltaTransformPoint( point_tr );
        point_r                            = m.deltaTransformPoint( point_r );
        
        // draw Branch
        graphics.beginFill( 0x804000 );
        graphics.moveTo( point_l.x, point_l.y );
        graphics.lineTo( point_tl.x, point_tl.y );
        graphics.lineTo( point_tr.x, point_tr.y );
        graphics.lineTo( point_r.x, point_r.y );        
        graphics.endFill();
        
        addEventListener( Event.ENTER_FRAME, update );
    }
    
    private function update( e:Event ):void 
    {
        // end grow by rnnning out Life
        if ( _life <= MIN_LIFE )
        {
            // last life growth leaves
            if ( 0 < _life )
            {
                var nyo_leaves:NyoLeaves        = new NyoLeaves( _life, _rotate );
                nyo_leaves.x                    = _branch_point.x;
                nyo_leaves.y                    = _branch_point.y;
                addChild( nyo_leaves );
            }
            
            removeEventListener( Event.ENTER_FRAME, update );
            return;
        }
        
        // cost Life
        _life                            -= 1 + Math.floor( Math.random() * LIFE_DEGENERATE_RATE );
        
        // Grow new Branch and cost life
        var nyo_tree:NyoTree            = new NyoTree( _life, _rotate );
        nyo_tree.x                        = _branch_point.x;
        nyo_tree.y                        = _branch_point.y;
        addChild( nyo_tree );
    }
}

class NyoLeaves extends Sprite
{
    private const LEAVES_SIZE_RATE:int        = 6;
    private const BOTTOM_COLOR:uint            = 0xffaaaa;
    private const TOP_COLOR:uint            = 0xffffff;
    
    public function NyoLeaves( size:int = 0, parent_rotate:int = -180 )
    {
        // adjust size
        size                            *= LEAVES_SIZE_RATE;
        
        // create curve points
        var point_b:Point                = new Point( 0, 0 );
        var point_al:Point                = new Point( - size / 2, 0 );
        var point_ar:Point                = new Point( size / 2, 0 );
        var point_t:Point                = new Point( 0, size );
        
        // apply Rotation
        var rotate:int                    = -60 + parent_rotate + Math.floor( Math.random() * 120 );
        var rm:Matrix                    = new Matrix();
        rm.rotate( rotate * Math.PI / 180 );
        var p_b:Point                    = rm.deltaTransformPoint( point_b );
        var p_al:Point                    = rm.deltaTransformPoint( point_al );
        var p_ar:Point                    = rm.deltaTransformPoint( point_ar );
        var p_t:Point                    = rm.deltaTransformPoint( point_t );
        
        // draw
        var gm:Matrix                    = new Matrix();
        gm.createGradientBox( size * 2, size * 2, 0, - size, - size );
        graphics.beginGradientFill( GradientType.RADIAL, [ BOTTOM_COLOR, TOP_COLOR ], [ 1, 0 ], [ 64, 255 ], gm );
        graphics.curveTo( p_al.x, p_al.y,  p_t.x,  p_t.y );
        graphics.curveTo( p_ar.x, p_ar.y, p_b.x, p_b.y );
        graphics.endFill();
    }
}
